//
//  IpcMessage.cpp
//  Because the wheel always needs to be reinvented *cough*...
//  This is a poor replacement for BMessage, everyones favorite data container
//  2006, John Wiggins
//
// This program is free software; you can redistribute it and/or modify  
// it under the terms of the GNU General Public License as published by  
// the Free Software Foundation; either version 2 of the License, or     
// (at your option) any later version.                                   
// 
// This program is distributed in the hope that it will be useful,       
// but WITHOUT ANY WARRANTY; without even the implied warranty of        
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         
// GNU General Public License for more details.                          
//     
//     You should have received a copy of the GNU General Public License     
//     along with this program; if not, write to the                         
//         Free Software Foundation, Inc.,                                       
//         59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.  
//-------------------------------------------------------------
#include "Ipc/IpcMessage.h"
//-------------------------------------------------------------
using std::vector;
//-------------------------------------------------------------
IpcMessage::IpcMessage( uint32 command )
: what( command )
{
    // nothing to do
}
//-------------------------------------------------------------
IpcMessage::IpcMessage( IpcMessage const & copy )
: what( copy.what )
{
    tDataRecord rec;
    
    // deep copy
    for ( uint32 id=0; id < copy.mData.size(); id++ )
    {
        // various attributes of this record
        rec.name = strdup(copy.mData[id].name);
        rec.type = copy.mData[id].type;
        rec.size = copy.mData[id].size;
        rec.capacity = copy.mData[id].capacity;

        // copy the data list
        rec.data = (tData *)malloc( sizeof( tData ) * copy.mData[id].size );
        for ( uint32 j=0; j < copy.mData[id].size; j++ )
        {
            // copy each item
            tData item = copy.mData[id].data[j];
            item.data = (uchar *)malloc(item.size);
            memcpy(item.data, copy.mData[id].data[j].data, item.size);

            rec.data[j] = item;
        }

        // add it to the vector
        mData.push_back( rec );
    }
}
//-------------------------------------------------------------
IpcMessage::~IpcMessage()
{
    // conveniently delete the contents
    MakeEmpty();
}
//-------------------------------------------------------------
status_t
IpcMessage::SendMessage( ipc_port_t port ) const
{
    char *buf = NULL;
    ssize_t size = 0;
    status_t ret;

    // make the buffer to hold the flat message
    size = FlattenedSize();
    buf = (char *)malloc( size );
    if ( buf == NULL )
    {
        return GE_NO_MEMORY;
    }
    
    // flatten the message
    Flatten( buf );

    // send the message
    ret = pp_write_port( port, what, buf, (int32)size );
    
    // free the message buffer (a copy was made in pp_write_port)
    free( buf );

    // return whatever pp_write_port returned
    return ret;
}
//-------------------------------------------------------------
// Add...() functions
status_t
IpcMessage::AddData( char const *name, type_code type, void const *data,
                        ssize_t numBytes, bool fixedSize, int32 numItems )
{
    uint32 index = 0;
    tData newData;

    // no names longer than 255 chars allowed
    if ( strlen( name ) > 255 )
    {
        return GE_BAD_VALUE;
    }
    
    // check to see if this name already exists
    for ( ; index < mData.size(); index++ )
    {
        // did we find it?
        if ( 0 == strcmp( mData[index].name, name ) )
        {
            // check for type mismatch
            if ( mData[index].type != type )
            {
                return GE_BAD_TYPE;
            }

            // ok, we found what we want
            break;
        }
    }

    // check for other bad types
    if ( type > BAS_RAW_TYPE )
    {
        return GE_BAD_TYPE;
    }

    // create the data container
    newData.data = (uchar *)malloc( numBytes );
    if ( !newData.data )
    {
        return GE_NO_MEMORY;
    }
    memcpy( newData.data, data, numBytes );
    newData.size = numBytes;

    // index should equal size if nothing was found
    if ( index == mData.size() )
    {
        // create a new item in mData
        tDataRecord newItem;
        
        newItem.name = strdup( name );
        newItem.type = type;
        newItem.data = (tData *)malloc( sizeof( tData ) * numItems );
        if ( newItem.data == NULL )
        {
            return GE_NO_MEMORY;
        }
        newItem.data[0] = newData;
        newItem.size = 1;
        newItem.capacity = numItems;

        // put it into mData
        mData.push_back( newItem );
    }
    else
    {
        // append to mData[index]
        
        // grow the array if needed
        if ( mData[index].size >= mData[index].capacity )
        {
            mData[index].capacity *= 2;
            // grow the buffer
            tData *p = (tData *)realloc( mData[index].data,
                                         sizeof( tData )*mData[index].capacity );
            // catch failures
            if ( p == NULL )
            {
                printf( "realloc() failure in IpcMessage::AddData()!\n" );
                return GE_NO_MEMORY;
            }
            // good to go
            mData[index].data = p;
        }
        // add the item
        mData[index].data[mData[index].size] = newData;
        // increase size by 1
        mData[index].size++;
    }

    return GE_NO_ERROR;
}
//-------------------------------------------------------------
status_t
IpcMessage::AddBool( char const *name, bool aBool )
{
    return AddData( name, BAS_BOOL_TYPE, (void const *)&aBool, sizeof( bool ), true, 1 );
}
//-------------------------------------------------------------
status_t
IpcMessage::AddInt8( char const *name, int8 anInt8 )
{
    return AddData( name, BAS_INT8_TYPE, (void const *)&anInt8, sizeof( int8 ), true, 1 );
}
//-------------------------------------------------------------
status_t
IpcMessage::AddInt16( char const *name, int16 anInt16 )
{
    return AddData( name, BAS_INT16_TYPE, (void const *)&anInt16, sizeof( int16 ), true, 1 );
}
//-------------------------------------------------------------
status_t
IpcMessage::AddInt32( char const *name, int32 anInt32 )
{
    return AddData( name, BAS_INT32_TYPE, (void const *)&anInt32, sizeof( int32 ), true, 1 );
}
//-------------------------------------------------------------
status_t
IpcMessage::AddInt64( char const *name, int64 anInt64 )
{
    return AddData( name, BAS_INT64_TYPE, (void const *)&anInt64, sizeof( int64 ), true, 1 );
}
//-------------------------------------------------------------
status_t
IpcMessage::AddFloat( char const *name, float aFloat )
{
    return AddData( name, BAS_FLOAT_TYPE, (void const *)&aFloat, sizeof( float ), true, 1 );
}
//-------------------------------------------------------------
status_t
IpcMessage::AddDouble( char const *name, double aDouble )
{
    return AddData( name, BAS_DOUBLE_TYPE, (void const *)&aDouble, sizeof( double ), true, 1 );
}
//-------------------------------------------------------------
status_t
IpcMessage::AddString( char const *name, char const *string )
{
    return AddData( name, BAS_STRING_TYPE, (void const *)string, strlen( string )+1, false, 1 );
}
//-------------------------------------------------------------
status_t
IpcMessage::AddPointer( char const *name, void const *pointer )
{
    return AddData( name, BAS_POINTER_TYPE, (void const *)&pointer, sizeof( void * ), true, 1 );
}
//-------------------------------------------------------------
// Find...(  ) functions
//-------------------------------------------------------------
status_t
IpcMessage::FindData( char const *name, type_code type, int32 index,
            void const **data, ssize_t *numBytes ) const
{
    uint32 id = 0;
    
    // search for name
    for ( ; id < mData.size(); id++ )
    {
        // did we find it?
        if ( 0 == strcmp( mData[id].name, name ) )
        {
            // check for type mismatch
            if ( mData[id].type != type )
            {
                return GE_BAD_TYPE;
            }
                
            // ok, we found what we want
            break;
        }
    }

    // did we find anything?
    if ( id == mData.size() )
    {
        return GE_NAME_NOT_FOUND;
    }

    // is the index in range?
    if ( mData[id].size <= ( uint32 )index )
    {
        return GE_BAD_INDEX;
    }
    
    // ok, fish the data out of what was found
    *numBytes = mData[id].data[index].size;
    if ( type == BAS_POINTER_TYPE )
    {
        // the data is the pointer, write it into the pointer passed in
        memcpy( data, mData[id].data[index].data, sizeof( void * ) );
    }
    else if ( type == BAS_STRING_TYPE )
    {
        // this message has the string, write its heap address into the pointer
        memcpy( data, &( mData[id].data[index].data ), sizeof( void * ) );
    }
    else
    {
        // all other types, write the data into the address provided
        memcpy( (void *)*data, mData[id].data[index].data, mData[id].data[index].size );
    }

    return GE_NO_ERROR;
}
//-------------------------------------------------------------
status_t
IpcMessage::FindData( char const *name, type_code type, void const **data, ssize_t *numBytes ) const
{
    return FindData( name, type, 0, data, numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindBool( char const *name, bool *aBool )
{
    ssize_t numBytes;
    return FindData( name, BAS_BOOL_TYPE, 0, (void const **)&aBool, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindBool( char const *name, int32 index, bool *aBool )
{
    ssize_t numBytes;
    return FindData( name, BAS_BOOL_TYPE, index, (void const **)&aBool, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindInt8( char const *name, int8 *anInt8 )
{
    ssize_t numBytes;
    return FindData( name, BAS_INT8_TYPE, 0, (void const **)&anInt8, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindInt8( char const *name, int32 index, int8 *anInt8 )
{
    ssize_t numBytes;
    return FindData( name, BAS_INT8_TYPE, index, (void const **)&anInt8, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindInt16( char const *name, int16 *anInt16 )
{
    ssize_t numBytes;
    return FindData( name, BAS_INT16_TYPE, 0, (void const **)&anInt16, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindInt16( char const *name, int32 index, int16 *anInt16 )
{
    ssize_t numBytes;
    return FindData( name, BAS_INT16_TYPE, index, (void const **)&anInt16, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindInt32( char const *name, int32 *anInt32 )
{
    ssize_t numBytes;
    return FindData( name, BAS_INT32_TYPE, 0, (void const **)&anInt32, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindInt32( char const *name, int32 index, int32 *anInt32 )
{
    ssize_t numBytes;
    return FindData( name, BAS_INT32_TYPE, index, (void const **)&anInt32, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindInt64( char const *name, int64 *anInt64 )
{
    ssize_t numBytes;
    return FindData( name, BAS_INT64_TYPE, 0, (void const **)&anInt64, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindInt64( char const *name, int32 index, int64 *anInt64 )
{
    ssize_t numBytes;
    return FindData( name, BAS_INT64_TYPE, index, (void const **)&anInt64, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindFloat( char const *name, float *aFloat )
{
    ssize_t numBytes;
    return FindData( name, BAS_FLOAT_TYPE, 0, (void const **)&aFloat, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindFloat( char const *name, int32 index, float *aFloat )
{
    ssize_t numBytes;
    return FindData( name, BAS_FLOAT_TYPE, index, (void const **)&aFloat, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindDouble( char const *name, double *aDouble )
{
    ssize_t numBytes;
    return FindData( name, BAS_DOUBLE_TYPE, 0, (void const **)&aDouble, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindDouble( char const *name, int32 index, double *aDouble )
{
    ssize_t numBytes;
    return FindData( name, BAS_DOUBLE_TYPE, index, (void const **)&aDouble, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindString( char const *name, char const **string )
{
    ssize_t numBytes;
    return FindData( name, BAS_STRING_TYPE, 0, (void const **)string, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindString( char const *name, int32 index, char const **string )
{
    ssize_t numBytes;
    return FindData( name, BAS_STRING_TYPE, index, (void const **)string, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindPointer( char const *name, void const **pointer )
{
    ssize_t numBytes;
    return FindData( name, BAS_POINTER_TYPE, 0, pointer, &numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::FindPointer( char const *name, int32 index, void const **pointer )
{
    ssize_t numBytes;
    return FindData( name, BAS_POINTER_TYPE, index, pointer, &numBytes );
}
//-------------------------------------------------------------
// Replace...() functions
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceData( char const *name, type_code type, int32 index,
                         void const *data, ssize_t numBytes )
{
    uint32 id = 0;
    
    // search for name
    for ( ; id < mData.size(); id++ )
    {
        // did we find it?
        if ( 0 == strcmp( mData[id].name, name ) )
        {
            // check for type mismatch
            if ( mData[id].type != type )
            {
                return GE_BAD_TYPE;
            }
                
            // ok, we found what we want
            break;
        }
    }

    // did we find anything?
    if ( id == mData.size() )
    {
        return GE_NAME_NOT_FOUND;
    }

    // is the index in range?
    if ( mData[id].size <= ( uint32 )index )
    {
        return GE_BAD_INDEX;
    }
    
    // ok, replace the data of what was found
    uchar *p = (uchar *)realloc( mData[id].data[index].data, numBytes );
    // fail if realloc fails
    if ( p == NULL )
    {
        return GE_NO_MEMORY;
    }
    // make the replacement final
    mData[id].data[index].size = numBytes;
    mData[id].data[index].data = p;
    memcpy( mData[id].data[index].data, data, numBytes );

    return GE_NO_ERROR;
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceData( char const *name, type_code type, void const *data,
                                        ssize_t numBytes )
{
    return ReplaceData( name, type, 0, data, numBytes );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceBool( char const *name, bool aBool )
{
    return ReplaceData( name, BAS_BOOL_TYPE, 0, (void const *)&aBool, sizeof( bool ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceBool( char const *name, int32 index, bool aBool )
{
    return ReplaceData( name, BAS_BOOL_TYPE, index, (void const *)&aBool, sizeof( bool ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceInt8( char const *name, int8 anInt8 )
{
    return ReplaceData( name, BAS_INT8_TYPE, 0, (void const *)&anInt8, sizeof( int8 ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceInt8( char const *name, int32 index, int8 anInt8 )
{
    return ReplaceData( name, BAS_INT8_TYPE, index, (void const *)&anInt8, sizeof( int8 ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceInt16( char const *name, int16 anInt16 )
{
    return ReplaceData( name, BAS_INT16_TYPE, 0, (void const *)&anInt16, sizeof( int16 ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceInt16( char const *name, int32 index, int16 anInt16 )
{
    return ReplaceData( name, BAS_INT16_TYPE, index, (void const *)&anInt16, sizeof( int16 ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceInt32( char const *name, int32 anInt32 )
{
    return ReplaceData( name, BAS_INT32_TYPE, 0, (void const *)&anInt32, sizeof( int32 ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceInt32( char const *name, int32 index, int32 anInt32 )
{
    return ReplaceData( name, BAS_INT32_TYPE, index, (void const *)&anInt32, sizeof( int32 ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceInt64( char const *name, int64 anInt64 )
{
    return ReplaceData( name, BAS_INT64_TYPE, 0, (void const *)&anInt64, sizeof( int64 ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceInt64( char const *name, int32 index, int64 anInt64 )
{
    return ReplaceData( name, BAS_INT64_TYPE, index, (void const *)&anInt64, sizeof( int64 ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceFloat( char const *name, float aFloat )
{
    return ReplaceData( name, BAS_FLOAT_TYPE, 0, (void const *)&aFloat, sizeof( float ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceFloat( char const *name, int32 index, float aFloat )
{
    return ReplaceData( name, BAS_FLOAT_TYPE, index, (void const *)&aFloat, sizeof( float ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceDouble( char const *name, double aDouble )
{
    return ReplaceData( name, BAS_DOUBLE_TYPE, 0, (void const *)&aDouble, sizeof( double ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceDouble( char const *name, int32 index, double aDouble )
{
    return ReplaceData( name, BAS_DOUBLE_TYPE, index, (void const *)&aDouble, sizeof( double ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceString( char const *name, char const *string )
{
    return ReplaceData( name, BAS_STRING_TYPE, 0, (void const *)string, strlen( string )+1 );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplaceString( char const *name, int32 index, char const *string )
{
    return ReplaceData( name, BAS_STRING_TYPE, index, (void const *)string, strlen( string )+1 );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplacePointer( char const *name, void const *pointer )
{
    return ReplaceData( name, BAS_POINTER_TYPE, 0, (void const *)pointer, sizeof( void * ) );
}
//-------------------------------------------------------------
status_t
IpcMessage::ReplacePointer( char const *name, int32 index, void const *pointer )
{
    return ReplaceData( name, BAS_POINTER_TYPE, index, (void const *)pointer, sizeof( void * ) );
}
//-------------------------------------------------------------
// flattening functions
//
//    Flattened Message Format:
//    - Header
//        - number of records : uint32
//        - message code : uint32
//    - Record 0
//        - length of name : uint8 ( 0-255 )
//        - name ( not NULL terminated )
//        - the type : uint32
//        - number of items : uint32
//        - item sizes : <number of items> X uint32
//        - item0, item1, item2, ... itemN
//    - Record 1
//        - ...
//    ...
//    - Record N
//
//-------------------------------------------------------------
status_t
IpcMessage::Flatten( char *address, ssize_t *numBytes ) const
{
    ssize_t byteswritten = 0;
    char *ptr = address;
    uint32 num;

    // flatten this sucker!
    
    // the number of records
    num = mData.size(  );
    memcpy( ptr, (void const *)&num, sizeof( uint32 ) );
    ptr += sizeof( uint32 );
    byteswritten += sizeof( uint32 );
    // the 'what' code
    num = this->what;
    memcpy( ptr, (void const *)&num, sizeof( uint32 ) );
    ptr += sizeof( uint32 );
    byteswritten += sizeof( uint32 );
    // each of the records
    for ( uint32 i = 0; i < mData.size(); i++ )
    {
        ssize_t tmp = FlattenRecord( ptr, i );
        ptr += tmp;
        byteswritten += tmp;
    }

    // assign numBytes if it's there
    if ( numBytes != NULL )
    {
        *numBytes = byteswritten;
    }
    
    return GE_NO_ERROR;
}
//-------------------------------------------------------------
status_t IpcMessage::Unflatten( char const *address )
{
    char *ptr = (char *)address;
    uint32 num;

    // empty the message
    MakeEmpty();

    // unflatten this sucker!
    
    // the number of records
    memcpy( (void *)&num, ptr, sizeof( uint32 ) );
    ptr += sizeof( uint32 );
    
    // the 'what' code
    memcpy( (void *)&( this->what ), ptr, sizeof( uint32 ) );
    ptr += sizeof( uint32 );
    
    // each of the records
    for ( uint32 i = 0; i < num; i++ )
    {
        status_t tmp = UnflattenRecord( ptr );
        
        // report any errors
        if ( tmp < GE_NO_ERROR )
        {
            return tmp;
        }
        
        ptr += tmp;
    }

    return GE_NO_ERROR;
}
//-------------------------------------------------------------
ssize_t
IpcMessage::FlattenedSize( void ) const
{
    ssize_t size = 0;

    // the number of records
    size += sizeof( uint32 );
    // the 'what' code
    size += sizeof( uint32 );
    // the sizes of the records
    for ( uint32 i=0; i < mData.size(); i++ )
    {
        size += NthFlattenedSize( i );
    }
    
    return size;
}
//-------------------------------------------------------------
ssize_t
IpcMessage::NthFlattenedSize( int32 index ) const
{
    ssize_t size = 0;
    
    // sizeof( uint8 ) to hold the length of the name
    size += sizeof( uint8 );
    // the name ( not including the NULL at the end )
    size += strlen( mData[index].name );
    // sizeof( uint32 ) to hold the type
    size += sizeof( uint32 );
    // sizeof( uint32 ) to hold the number of items
    size += sizeof( uint32 );
    // <number of items> * sizeof( uint32 ) for the size array
    size += mData[index].size * sizeof( uint32 );
    // and then the sizes of all the items added up
    for ( uint32 i = 0; i < mData[index].size; i++ )
    {
        size += mData[index].data[i].size;
    }

    return size;
}
//-------------------------------------------------------------
ssize_t
IpcMessage::FlattenRecord( char *address, int32 index ) const
{
    ssize_t byteswritten = 0;
    char *ptr = address;
    uint8 len = strlen( mData[index].name );
    uint32 num, i;

    // the length of the name
    memcpy( ptr, (void const *)&len, sizeof( uint8 ) );
    ptr += sizeof( uint8 );
    byteswritten += sizeof( uint8 );
    
    // the name
    memcpy( ptr, mData[index].name, len );
    ptr += len;
    byteswritten += len;
    
    // the type
    num = mData[index].type;
    memcpy( ptr, (void const *)&num, sizeof( uint32 ) );
    ptr += sizeof( uint32 );
    byteswritten += sizeof( uint32 );
    
    // the number of items
    num = mData[index].size;
    memcpy( ptr, (void const *)&num, sizeof( uint32 ) );
    ptr += sizeof( uint32 );
    byteswritten += sizeof( uint32 );
    
    // the item sizes array
    for ( i=0; i < mData[index].size; i++ )
    {
        num = mData[index].data[i].size;
        memcpy( ptr, (void const *)&num, sizeof( uint32 ) );
        ptr += sizeof( uint32 );
        byteswritten += sizeof( uint32 );
    }
    
    // the items
    for ( i=0; i < mData[index].size; i++ )
    {
        memcpy( ptr, mData[index].data[i].data, mData[index].data[i].size );
        ptr += mData[index].data[i].size;
        byteswritten += mData[index].data[i].size;
    }

    return byteswritten;
}
//-------------------------------------------------------------
status_t
IpcMessage::UnflattenRecord( char *address )
{
    tDataRecord rec;
    int32 bytesread = 0;
    char *ptr = address;
    uint8 len;
    uint32 i;

    // the length of the name
    memcpy( (void *)&len, ptr, sizeof( uint8 ) );
    ptr += sizeof( uint8 );
    bytesread += sizeof( uint8 );
    
    // the name ( remember space for the NULL terminator )
    rec.name = (char *)malloc( len+1 );
    memcpy( rec.name, ptr, len );
    rec.name[len] = '\0';
    ptr += len;
    bytesread += len;
    
    // the type
    memcpy( (void *)&( rec.type ), ptr, sizeof( uint32 ) );
    ptr += sizeof( uint32 );
    bytesread += sizeof( uint32 );
    
    // the number of items
    memcpy( (void *)&( rec.size ), ptr, sizeof( uint32 ) );
    ptr += sizeof( uint32 );
    bytesread += sizeof( uint32 );
    
    // pause briefly to allocate the items array
    rec.data = (tData *)malloc( sizeof( tData ) * rec.size );
    if ( rec.data == NULL )
    {
        return GE_NO_MEMORY;
    }
    
    // the item sizes array
    for ( i=0; i < rec.size; i++ )
    {
        // read the size
        memcpy( (void *)&( rec.data[i].size ), ptr, sizeof( uint32 ) );
        ptr += sizeof( uint32 );
        bytesread += sizeof( uint32 );
        // touch the data pointer while we're passing through
        rec.data[i].data = NULL;
    }

    // the items
    for ( i=0; i < rec.size; i++ )
    {
        rec.data[i].data = (uchar *)malloc( rec.data[i].size );
        if ( rec.data[i].data == NULL )
        {
            // add the record anyway so that its memory gets cleaned up
            // ( this might fail too )
            mData.push_back( rec );
            return GE_NO_MEMORY;
        }

        memcpy( rec.data[i].data, ptr, rec.data[i].size );
        ptr += rec.data[i].size;
        bytesread += rec.data[i].size;
    }

    // add the record to mData
    mData.push_back( rec );

    return bytesread;
}
//-------------------------------------------------------------
// emptiness functions
status_t
IpcMessage::MakeEmpty( void )
{
    // free the data, if any
    for ( vector<tDataRecord>::iterator i = mData.begin(); i != mData.end(); i++ )
    {
        // free the data list
        for ( uint32 j=0; j < i->size; j++ )
        {
            // free each item
            free( i->data[j].data );
        }
        // then the list itself
        free( i->data );

        // free the name
        free( i->name );
    }

    // the clear the vector container
    mData.clear();

    return GE_NO_ERROR;
}
//-------------------------------------------------------------
bool
IpcMessage::IsEmpty( void ) const
{
    return ( mData.size() == 0 );
}
//-------------------------------------------------------------
static char const
*typeStr( type_code type )
{
    switch ( type )
    {
        case BAS_CHAR_TYPE:
            return "BAS_CHAR_TYPE";
        case BAS_BOOL_TYPE:
            return "BAS_BOOL_TYPE";
        case BAS_INT8_TYPE:
            return "BAS_INT8_TYPE";
        case BAS_INT16_TYPE:
            return "BAS_INT16_TYPE";
        case BAS_INT32_TYPE:
            return "BAS_INT32_TYPE";
        case BAS_INT64_TYPE:
            return "BAS_INT64_TYPE";
        case BAS_FLOAT_TYPE:
            return "BAS_FLOAT_TYPE";
        case BAS_DOUBLE_TYPE:
            return "BAS_DOUBLE_TYPE";
        case BAS_SIZE_T_TYPE:
            return "BAS_SIZE_T_TYPE";
        case BAS_SSIZE_T_TYPE:
            return "BAS_SSIZE_T_TYPE";
        case BAS_POINTER_TYPE:
            return "BAS_POINTER_TYPE";
        case BAS_STRING_TYPE:
            return "BAS_STRING_TYPE";
        case BAS_RAW_TYPE:
            return "BAS_RAW_TYPE";
        default:
            break;
    }

    return "";
}
//-------------------------------------------------------------
static void
hexDump( uchar *buf, ssize_t size )
{
    printf( "\t" );
    for ( ssize_t i=0; i < size; i++ )
    {
        printf( " %02X", buf[i] );
        if ( ( i % 25 ) == 0 && i != 0 )
        {
            printf( "\n\t" );
        }
    }
}
//-------------------------------------------------------------
void
IpcMessage::PrintToStream() const
{
    printf( "IpcMessage:\nwhat = %x\n", this->what );
    for ( uint32 i = 0; i < mData.size(); i++ )
    {
        printf( "data[%d]: \"%s\" : %s\n", i, mData[i].name, typeStr( mData[i].type ) );
        for ( uint32 j=0; j < mData[i].size; j++ )
        {
            printf( "data[%d][%d]:\n", i,j );
            hexDump( mData[i].data[j].data, mData[i].data[j].size );
            printf( "\n" );
        }
    }
}
//-------------------------------------------------------------
// removal of data
status_t
IpcMessage::RemoveName( char const *name )
{
    vector<tDataRecord>::iterator id;
    
    // search for name
    for ( id = mData.begin(); id != mData.end(); id++ )
    {
        // did we find it?
        if ( 0 == strcmp( id->name, name ) )
        {
            // ok, we found what we want
            break;
        }
    }

    // did we find anything?
    if ( id == mData.end() )
    {
        return GE_NAME_NOT_FOUND;
    }

    // remove the found item's items
    for ( uint32 j=0; j < id->size; j++ )
    {
        // free each item
        free( id->data[j].data );
    }
    free( id->data );

    // erase the item from the vector
    mData.erase( id );
    
    return GE_NO_ERROR;
}
//-------------------------------------------------------------
status_t
IpcMessage::RemoveData( char const *name, int32 index )
{
    vector<tDataRecord>::iterator id;
    
    // search for name
    for ( id = mData.begin(); id != mData.end(); id++ )
    {
        // did we find it?
        if ( 0 == strcmp( id->name, name ) )
        {
            // ok, we found what we want
            break;
        }
    }

    // did we find anything?
    if ( id == mData.end() )
    {
        return GE_NAME_NOT_FOUND;
    }

    // is the index in range?
    if ( id->size <= index || index < 0 )
    {
        return GE_BAD_INDEX;
    }

    // remove the found item's item
    free( id->data[index].data );

    // adjust the rest of the items to maintain contiguity
    for ( uint32 j=index+1; j < id->size; j++ )
    {
        // move data @ index j to index j-1
        id->data[j-1] = id->data[j];
    }

    // decrement size
    id->size -= 1;

    // erase the item from the vector if it is empty
    if ( id->size == 0 )
    {
        mData.erase( id );
    }
    
    return GE_NO_ERROR;
}
//-------------------------------------------------------------
